

/**
 * 关于Lua的栈
 * 1 为栈底
 * -1 为栈顶
 * 1 2 3 4 5 6 7 8 9 自底向上
 * -1 -2 -3 -4 -5 -6 自顶向下
*/

use mlua::prelude::*;




mod lua {
    pub static mut LUA: ::once_cell::sync::Lazy<mlua::Lua>
        = ::once_cell::sync::Lazy::new(|| mlua::Lua::new());

    
    pub trait LuaTableTrait {
        fn add_method<'lua, A, R, F>(&self, lua: &'lua mlua::Lua, method_name: &str, func: F) -> mlua::Result<()>
            where
                A: mlua::FromLuaMulti<'lua>,
                R: mlua::ToLuaMulti<'lua>,
                F: 'static + Fn(&'lua mlua::Lua, A) -> mlua::Result<R>;
    }
    impl LuaTableTrait for mlua::Table<'_>
    {
        fn add_method<'lua, A, R, F>(&self, lua: &'lua mlua::Lua, method_name: &str, func: F) -> mlua::Result<()>
            where
                A: mlua::FromLuaMulti<'lua>,
                R: mlua::ToLuaMulti<'lua>,
                F: 'static + Fn(&'lua mlua::Lua, A) -> mlua::Result<R>
        {
            let func_lua = lua.create_function(func)?;
            self.set(method_name, func_lua)?;
            Ok(())
        }
    }


    pub fn get_lua() -> &'static mlua::Lua { unsafe { &LUA } }
}


mod animation {
    use super::super::animation::*;

    use super::lua::{self, LuaTableTrait};

    impl mlua::UserData for AnimationManager {
        /// Adds custom fields specific to this userdata.
        fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(_fields: &mut F)
        {
        
        }

        /// Adds custom methods and operators specific to this userdata.
        fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(_methods: &mut M)
        {
            
        }
    }
    


    fn init_animation_manager(lua: &'static mlua::Lua)
    {
        let table = lua.create_table()
            .expect("tolua.rs animation::init_animation_manager error");

        table.add_method(
            lua,
            "new",
            |_, ()| {
                Ok(AnimationManager::new())
            }
        ).expect("table add method error");


        lua.globals().set("AnimationManager", table)
            .expect("tolua.rs animation::init_animation_manager lua.globals().set() error");
    }

    pub fn init(lua: &'static mlua::Lua)
    {
        init_animation_manager(lua);
    }

}


mod character {
    use super::super::character::*;

    use super::lua::LuaTableTrait;

    impl mlua::UserData for GameCharacter {
        /// Adds custom fields specific to this userdata.
        fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(_fields: &mut F)
        {

        }

        /// Adds custom methods and operators specific to this userdata.
        fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(_methods: &mut M)
        {
            
        }
    }


    fn init_game_character_manager(lua: &mlua::Lua)
    {
        let table = lua.create_table()
            .expect("tolua.rs animation::init_animation_manager error");

        table.add_method(
            lua,
            "new",
            |_, ()| {
                Ok(GameCharacterManager::new())
            }
        ).expect("table add method error");

        lua.globals().set("CharacterManager", table);
    }

    pub fn init(lua: &mlua::Lua)
    {
        init_game_character_manager(lua);
    }
}

unsafe fn init() -> LuaResult<()>
{
    animation::init(&lua::LUA);
    Ok(())
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_0() -> LuaResult<()>
    {
        let lua = Lua::new();

        let map_table = lua.create_table()?;
        map_table.set(1, "one")?;
        map_table.set("two", 2)?;

        lua.globals().set("map_table", map_table)?;

        lua.load("for k,v in pairs(map_table) do print(k,v) end").exec()?;

        Ok(())
    }


    #[test]
    fn test_init() -> LuaResult<()>
    {
        unsafe { init() }
    }


    #[cfg(test)]
    mod test_animation {
        use super::*;


        #[test]
        fn test_init() -> LuaResult<()>
        {
            let lua = lua::get_lua();
            animation::init(lua);

            //test init AnimationManager
            lua.load(
                "
                AnimationManager.__index = AnimationManager;
                local manager = AnimationManager;
                print(AnimationManager);
                print(manager);
                local m = manager.new();
                print(m);
                local m2 = m:new();
                print(m2);
                local m3 = m.new();
                print(m3)
                "
            ).exec()?;
            Ok(())
        }
    }
}


